// wglfont.cpp
//

#include "config.h"
#include "wgl/wglfont.hpp"
#include "wgl/wgl_gl.hpp"
#include "wgl/wglutils.hpp"

#include "stb/stb_truetype.h"

using namespace wgl;



struct WGL_API GlyphImage
{
  int width;
  int height;
};



// Font::Font
//
Font::Font ( std::string path,
             int size )
  : path(path),
    size(size)
{
  // WGL_TRACE("loading font %s", path.c_str());

  WGL_ASSERT(!font_info);
  font_info = new stbtt_fontinfo;
  
  font_buffer = wgl::utils::read_file(path);
  font_buffer.shrink_to_fit();
  
  stbtt_InitFont(font_info,
                 (unsigned char *) font_buffer.data(),
                 stbtt_GetFontOffsetForIndex((unsigned char *) font_buffer.data(), 0));

  scale = stbtt_ScaleForPixelHeight(font_info, size);
  stbtt_GetFontVMetrics(font_info, &ascent, &descent, &line_gap);

  baseline = int(float(ascent) * scale); // ?

  s_ascent = float(ascent) * scale;
  s_descent = float(descent) * scale;
  
  WGL_TRACE("font loaded: %s (size=%d, scale=%.3f, ascent=%d, descent=%d, line_gap=%d, baseline=%d",
            path.c_str(), size, scale, ascent, descent, line_gap, baseline);
}



// Font::~Font
//
Font::~Font ()
{
  delete font_info;
}



// Font::load_glyph_metrics
//
void Font::load_glyph_metrics ( GlyphImage &glyph,
                                int c )
{
  if (glyph.metrics_loaded)
    return;
  
  // [FIXME] should be in some other func
  stbtt_GetCodepointHMetrics(font_info,
                             c,
                             &glyph.advance,
                             &glyph.lsb);

  glyph.s_advance = scale * float(glyph.advance);
  
  stbtt_GetCodepointBitmapBox(font_info,
                              c,
                              scale,
                              scale,
                              &glyph.ix0,
                              &glyph.iy0,
                              &glyph.ix1,
                              &glyph.iy1);  

  // WGL_TRACE("char '%c' x %d -> %d,%d,%d,%d adv=%d, lsb=%d",
  //           c,
  //           size,
  //           glyph.ix0,
  //           glyph.iy0,
  //           glyph.ix1,
  //           glyph.iy1,
  //           glyph.advance,
  //           glyph.lsb);

  glyph.metrics_loaded = 1;
}



// Font::render_glyph
//
void Font::render_glyph ( GlyphImage &glyph,
                          int c )
{
  if (glyph.rendered)
    return;
  load_glyph_metrics(glyph, c);
  
  unsigned char *bitmap;
  int width, height;
  
  bitmap = stbtt_GetCodepointBitmap(font_info,
                                    0,
                                    scale,
                                    c,
                                    &width,
                                    &height,
                                    0,
                                    0);
  
  size_t offset = images.size();
  size_t bytes_size = sizeof(GlyphImage) + width * height * 4;

  images.resize(offset + bytes_size);

  unsigned char *dest = images.data() + offset;
  for (int j = 0; j < height; j++)
    {
      unsigned char *pixel = bitmap + (height - j - 1) * width;
      for (int i = 0; i < width; i++)
        {
          *dest++ = 0x00;
          *dest++ = 0x00;
          *dest++ = 0x00;
          *dest++ = *pixel;
          pixel++;
        }
    }

  glyph.rendered = 1;
  glyph.width = width;
  glyph.height = height;
  glyph.offset = offset;
}



// Font::get_text_size
//
void Font::get_text_size ( const std::string &text,
                           int &width,
                           int &height )
{
  height = size; // [FIXME] ?
  float x = 0.f;
  for (const char *c = text.c_str(); *c; ++c)
    {
      GlyphImage &glyph = glyphs[size_t(*c)];
      load_glyph_metrics(glyph, *c);
      x += glyph.s_advance;
      if (c[1])
        x += scale * stbtt_GetCodepointKernAdvance(font_info, c[0], c[1]);
    }
  width = int(std::ceil(x));
}



// Font::render_text_image
//
void Font::render_text_image ( const std::string &text,
                               float x,
                               float y )
{
  // [FIXME]
  gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);

  gl.Enable(GL_BLEND);
  gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  gl.Color3f(0.f, 0.f, 0.f); // ?

  for (const char *c = text.c_str(); *c; ++c)
    {
      GlyphImage &glyph = glyphs[size_t(*c)];
      render_glyph(glyph, *c);

      gl.RasterPos2f(x, y - float(glyph.iy1));
      gl.DrawPixels(glyph.width,
                    glyph.height,
                    GL_RGBA,
                    GL_UNSIGNED_BYTE,
                    images.data() + glyph.offset);

      x += glyph.s_advance;
      if (c[1])
        x += scale * stbtt_GetCodepointKernAdvance(font_info, c[0], c[1]);

      // [TODO]
      // STBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
      // an additional amount to add to the 'advance' value between ch1 and ch2
    }

  gl.Disable(GL_BLEND);
}
